#include "xNodeType_Clock.h"
#include "xParameterPack.h"
#include "animationCenter.h"
#include "vmLibrary.h"
#include "all_entries.h"
#include "MappingEngine.h"
#include "xNodeType_WOP.h"
#include "xOutMappingPort.h"

xNodeType_Clock::xNodeType_Clock(){
	low_bound = new int(0);
	upper_bound = new int(0);
	increment = new int(0);
	time_interval = new double(0.0);
	counter = 0.0;
	_wop = NULL;
	_name = "clock" ;
}

xNodeType_Clock::~xNodeType_Clock() {
}

void xNodeType_Clock::setupPorts(){
	assert(_parent->getType() == "clock");
	xMappingNodeTypeDescriptor* _vd = xMappingNodeTypeLibrary::getInstance()->get_xmnt_descriptor("clock");
	for(int i=_vd->in_para_no-4 ;i < _vd->in_para_no ;i++){
		xInMappingPort *ip = new xInMappingPort(_parent,
								_vd->in_para_name[i],
								_vd->in_para_type[i],
								static_cast< MNClass >( _vd->in_para_class[i] ),
								_vd->in_default_value[i],
								static_cast< MNCategory >( _vd->in_para_category[i] ) );
		_parent->_inports.push_back(ip);
	}
	
	_parent->_outports.push_back(new xOutMappingPort(_parent,"int*","int*",CLASS_REQUIRED,"NULL",CATEGORY_OTHER));
}

void xNodeType_Clock::createMappedObject(){
	animationCenter::getInstance()->addAnimation(this);
	xMappingNode *node = this->getParent();

	// Many objects may connect to this mapping node. 
	// Now, we collect all the objects and pack it into a xParameterPack object.
	_xpp.no = 0 ;
	for (int i = 0 ; i < node->getInportSize(); i++) {
		// get the nodetype pointer that connects to this inport (unique)
		
		xInMappingPort *inp = node->getInPort(i);
		xParameter xp = inp->toXParameter();
		_xpp.no ++;
		_xpp.paras.push_back(xp);
		if (inp->getLink() != NULL) _xpp.para_onoff.push_back(true);
		else _xpp.para_onoff.push_back(false);
	}
	init_wop();
}

xParameter xNodeType_Clock::toXParameter(string type, xOutMappingPort *linked_op){
	xParameter xp ;
	if (type == "wop") {
		assert(false);
		xp.type = T_WOP ;
		xp.wop_nodetype = this ;
		xp.val_wop = _wop ;
	} else if (type == "varname") {
		xp.type = T_STRING ;
		xp.val_string = _wop->getName() ;
	} else if (type == "int*") {
		xp.type = TP_INT ;
		WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
		assert(iwop != NULL) ;
		xp.val_int_ptr = iwop->getIntValAddr();
		//xp.original = iwop->getVal();
	} else if (type == "int") {
		xp.type = T_INT ;
		WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
		assert(iwop != NULL) ;
		int *temp = iwop->getIntValAddr();
		xp.val_int = (*temp);
		//xp.original = iwop->getVal();
	} else if (type == "bool*") {
		xp.type = TP_BOOL ;
		WOP_Bool_Proxy * bwop = dynamic_cast<WOP_Bool_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_bool_ptr = bwop->getBoolValAddr();
		//xp.original = bwop->getVal();
	} else if (type == "float*") {
		xp.type = TP_FLOAT ;
		WOP_Float_Proxy * bwop = dynamic_cast<WOP_Float_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_float_ptr = bwop->getFloatValAddr();
		//xp.original = bwop->getVal();
	} else if (type == "double*") {
		xp.type = TP_DOUBLE ;
		WOP_Double_Proxy * bwop = dynamic_cast<WOP_Double_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_double_ptr = bwop->getDoubleValAddr();
		//xp.original = bwop->getVal();
	} else if (type == "char*") {
		xp.type = TP_CHAR ;
		WOP_Char_Proxy * bwop = dynamic_cast<WOP_Char_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_char_ptr = bwop->getCharValAddr();
		//xp.original = bwop->getVal();
	} else if (type == "ints") { 
		// this is the case where a composite VM asking for dimension
		assert(dynamic_cast<WOP_Array_Entry*>(_wop) != NULL) ;
		WOP_Array_Entry * ary = (WOP_Array_Entry*) _wop ;
		xp.type = T_INTS ;
		xp.val_ints = ary->getDimensionRange();
	} else if (type == "string") {
		xp.type = T_STRING ;
		xp.val_string = _wop->getVal();
		//xp.original = _wop->getVal();
	} else if (type == "string*") {
		xp.type = TP_STRING ;
		xp.val_string_ptr = _wop->getValAddr();
		//xp.original = _wop->getVal();
	} else if (type == "tostring*") {
		xp.type = TP_STRING ;
		xp.val_string_ptr = _wop->getValAddr();
		//xp.original = _wop->getVal();
	} else 
		assert(false);
	// This assertion false occurs due to
	return xp;
}

string xNodeType_Clock::toMDString(MDSType type){
	//assert(false); // need to be written here
	// under construction, please see xNodeType_Math for example
	ostringstream MDSstr ;
	xParameter xp ;
	int inp_no = 4 ; 

	if ( type == xNodeType::NormalMDSFormat){
		MDSstr << "node "<<  _parent->getSerialNo() << " clock "<< inp_no << endl ; 
	} else if ( type == xNodeType::ParameterizedMDSFormat ){
		MDSstr << "ADD_NODE " << "node "<< "SN" <<_parent->getSerialNo() << " clock "<< inp_no << endl ; 
	} else {
		assert(false);
	}
	
	MDSstr << "  defaultstring<" ;

	xMappingNode *node = this->getParent();

	for (int i = 0 ; i < node->getInportSize(); i++) {
		// get the nodetype pointer that connects to this inport (unique)
		xInMappingPort *inp = node->getInPort(i);
		MDSstr << " " << inp->getDefaultValue();
	}
	MDSstr << " >" << endl ;
	return MDSstr.str();
}
int  xNodeType_Clock::parseMDString(vector<string>& tokens, int start_index) {
	int i = start_index ;
	assert(tokens[i] == "node");
	string serialno = tokens[i+1] ;
	assert(tokens[i+2] == "clock");
	string vmname = tokens[i+2] ; // nodetypes are "wop" "vm" "vmcollector"
	string inport_no = tokens[i+3] ;

	istringstream buf2(inport_no);
	int inp_no ;
	buf2 >> inp_no ;

	assert(tokens[i+4] == "defaultstring<") ;
	//vmdescriptor* vd = this->get_vmdescriptor();
	//assert(vd->in_para_no == inp_no) ;

	i+=5 ;
	for (int k=0; k < inp_no; k++) {
		xMappingNode* p = this->getParent();
		xInMappingPort* in = p->getInPort(k);
		in->setDefaultValue(tokens[i+k]);
	}
	assert(tokens[i+inp_no] == ">"); 
	return inp_no+6 ; // 5 tokens begins with "node", defaultstring< is one token
	// and > is the last tokens ;
}
bool xNodeType_Clock::update() {
	if(variation >= (*upper_bound)){
		variation = *(low_bound);
	}
	ostringstream oss;
	oss << variation;
	assert(_wop != NULL);
	((WOP_Int_Proxy*)_wop)->updateProxyValue(oss.str());
	return true ;
}
void xNodeType_Clock::behavior(float timeSinceLastFrame){
	variation += (*increment)*timeSinceLastFrame/(*time_interval);
	
	xMappingTree * xmt = MappingEngine::getInstance()->getRootxMappingTree(this->getParent());
	xmt->update();
}

void xNodeType_Clock::init_wop(){
	low_bound = _xpp.paras[0].val_int_ptr;
	upper_bound = _xpp.paras[1].val_int_ptr;
	increment = _xpp.paras[2].val_int_ptr;
	time_interval = _xpp.paras[3].val_double_ptr;
	variation = (*low_bound);
	ostringstream oss;
	oss << variation;
	assert(_wop == NULL);
	if(_wop == NULL){		
		_wop = new WOP_Int_Proxy(new WOP_Int_Entry(oss.str(),NULL));
	}
	
	_wop->updateRealValue(oss.str());
}
string xNodeType_Clock::valid_rhs_pair[][2] = { // the left is the woptype of this node, right is the link that connects to
	"int","int*",
	"double","double*",
	"wop","int*",
	"wop","double*",
	"dontcare","int*",
	"dontcare","double*"
};
bool xNodeType_Clock::verify_rhs_link(xOutMappingPort *left, xInMappingPort *right, xNodeType* rightnode) {

	if (_wop == NULL) { // in many cases, the mapping node may not have a _wop object yet
		// before the link is checked in this case, we check
		if (right->getType() == "int*")
			return true ;
	}
	string wop_type = _wop->getTypeStr() ;
	return check_valid_pair(valid_rhs_pair, sizeof(valid_rhs_pair)/(sizeof(string)*2),
		wop_type, right->getType());
}
string xNodeType_Clock::gate_pair[][2] = {
	"wop","int*",
	"wop","double*",
	"int","int*",
	"double","double*",
	"int*","int*",
	"double*","double*",
	"dontcare","int*",
	"dontcare","double*",
	"dontcare","wop"
};
string xNodeType_Clock::wop_pair[][2] = { // the left is the woptype of this node, right is the link that connects to
	"wop","int*",
	"wop","double*",
	"dontcare","int*",
	"dontcare","double*",
	"dontcare","int",
	"dontcare","double",
	"dontcare","wop"
};
string xNodeType_Clock::other_pair[][2] = {
	"wop","int*",
	"wop","double*",
	"int","int*",
	"double","double*",
	"dontcare","int*",
	"dontcare","double*",
	"dontcare","wop"
};

bool xNodeType_Clock::verify_lhs_link(xOutMappingPort *left, xNodeType* leftnode_nt, xInMappingPort *right) {
	if (leftnode_nt->getType() == "gate") {
		// if the lhs is a gate port, 
		return check_valid_pair(gate_pair,sizeof(gate_pair)/(sizeof(string)*2),
			"dontcare",left->getType());
	}
	if (leftnode_nt->getType() == "wop") { // if the leftnode is a wop, we want to check if
		//   its type is int,
		xNodeType_WOP *wopnt = (xNodeType_WOP*)(leftnode_nt);
		WOP_Entry *wop = wopnt->getWOP();
		return check_valid_pair(wop_pair,sizeof(wop_pair)/(sizeof(string)*2),
			"dontcare",wop->getTypeStr());
	}
	// to here left is often the outport of other mapping node
	// we limit those ports to be of type int*, double*,
	return check_valid_pair(other_pair,sizeof(other_pair)/(sizeof(string)*2),
		"dontcare",left->getType());
}